package com.ruoyi.common.mongo.dao.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.ruoyi.common.annotation.Transient;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BaseIsDeletedEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.mongo.dao.MongoBasicDao;
import com.ruoyi.common.mongo.domain.po.MongoBasePO;
import com.ruoyi.common.mongo.domain.qo.MongoBaseQO;
import com.ruoyi.common.utils.StringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public abstract class MongoBasicDaoImpl<P extends MongoBasePO, Q extends MongoBaseQO<P>> implements MongoBasicDao<P, Q> {
    @Resource
    private MongoTemplate mongoTemplate;
    private Class<P> pClass;
    private Class<Q> qClass;

    @PostConstruct
    @SneakyThrows
    public void init() {
        this.pClass = (Class<P>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        this.qClass = (Class<Q>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
    }

    @Override
    public  TableDataInfo pageQuery(Q q) {
        Query query = new Query();
        Integer page = q.getPage();
        if(page==null||page<=0) {
            page = 1;
        }
        Integer size = q.getSize();
        if (size == null||size<=0) {
            size = 10;
        }
        // 设置分页
        query.skip((long) page * size).limit(size);

        // 设置排序
//        applySort(query, sort);
        buildWhere(query, q);
        // 查询数据
        List<P> content = mongoTemplate.find(query, pClass);

        // 查询总数
        long total = mongoTemplate.count(Query.of(query).limit(-1).skip(-1), pClass);
        TableDataInfo tableDataInfo = TableDataInfo.builder().rows( content).total(total).build();
        return tableDataInfo;
    }
    private void applySort(Query query, String sort) {
        if (!StringUtils.hasText(sort)) {
            return;
        }

        Sort sortObj = parseSort(sort);
        if (sortObj != null) {
            query.with(sortObj);
        }
    }

    @Override
    public P selectById(Object id){
        Query query = Query.query(Criteria.where("id").is(id));
        return mongoTemplate.findOne(query, pClass);
    }
    @Override
    public P selectExistsById(Object id){
        Query query = Query.query(Criteria.where("id").is(id).and("is_deleted").is(BaseIsDeletedEnum.EXISTS));
        return mongoTemplate.findOne(query, pClass);
    }

    @Override
    public int deleteById(Object id){
        Query query = Query.query(Criteria.where("id").is(id));
        return (int)mongoTemplate.remove(query, pClass).getDeletedCount();
    }

    @Override
    public int logicDeleteById(P p){
        if (p.getGmtModified() == null) {
            p.setGmtModified(new Date());
        }
        if (p.getId() == null) {
            throw new ServiceException("ID不能为空" , HttpStatus.PARAM_NOT_ERROR);
        }
        p.setIsDeleted( BaseIsDeletedEnum.DELETED);
        Query query = Query.query(Criteria.where("id").is(p.getId()));
        Update update = new Update();
        update.set("is_deleted", BaseIsDeletedEnum.DELETED);
        return (int)mongoTemplate.updateMulti(query, update, pClass).getModifiedCount();
    }
    /**
     * 解析排序字符串
     */
    private Sort parseSort(String sort) {
        if (!StringUtils.hasText(sort)) {
            return null;
        }

        List<Sort.Order> orders = new ArrayList<>();
        String[] sortPairs = sort.split(";");

        for (String pair : sortPairs) {
            String[] parts = pair.split(",");
            if (parts.length == 2) {
                String field = parts[0].trim();
                String direction = parts[1].trim().toUpperCase();

                if ("ASC".equals(direction)) {
                    orders.add(Sort.Order.asc(field));
                } else if ("DESC".equals(direction)) {
                    orders.add(Sort.Order.desc(field));
                }
            }
        }

        return orders.isEmpty() ? null : Sort.by(orders);
    }

    @Override
    public List<P> select(Q q) {
        Query query = buildWhere(q);
        return mongoTemplate.find(query, pClass);
    }

    @Override
    public int insert(P p) {
        P save = mongoTemplate.save(p);
        int isSuccess = save.getId() != null ?1:0;
        p.setId(save.getId());
        return isSuccess;
    }

    @Override
    public int batchInsert(List<P> ps) {
        Collection<P> ps1 = mongoTemplate.insertAll(ps);
        // 检查是否有插入失败的
        if (ps1.size() < ps.size()) {
            Set<Object> savedSet = ps1.stream()
                    .map(P::getId)
                    .collect(Collectors.toSet());

            log.info("部分插入失败，成功插入的ID有: {}", savedSet);
        }
        return ps1.size();
    }

    @Override
    public int update(Q q) {
        Query query = buildWhere(q);
        Update update = buildUpdate(q.getUpdatePO(),false);
        long modifiedCount = mongoTemplate.updateMulti(query, update, update.getClass()).getModifiedCount();
        return (int)modifiedCount;
    }

    @Override
    public int updateSelective(Q q) {
        Query query = buildWhere(q);
        Update update = buildUpdate(q.getUpdatePO(),true);
        long modifiedCount = mongoTemplate.updateMulti(query, update, update.getClass()).getModifiedCount();
        return (int)modifiedCount;
    }
    @Override
    public int batchUpdate(List<Q> ps) {
        int i = 0;
        for (Q p : ps) {
            this.update(p);
            i++;
        }
        return i;
    }

    @Override
    public int batchUpdateSelective(List<Q> ps) {
        int i = 0;
        for (Q p : ps) {
            this.updateSelective(p);
            i++;
        }
        return i;
    }

    @Override
    public int delete(Q q) {
        Query query = buildWhere(q);
        return (int)mongoTemplate.remove(query, pClass).getDeletedCount();
    }
    private Query buildWhere(Q q) {
        return this.buildWhere(null, q);
    }
    private <T> Update buildUpdate(P updateObj,boolean isSelective) {
        if (updateObj == null) {
            throw new IllegalArgumentException("更新对象不能为空");
        }

        Update update = new Update();
        List<Field> poFields = Arrays.stream(ReflectUtil.getFields(updateObj.getClass())).collect(Collectors.toList());
        int i=0;
        for (Field qoField : poFields) {
            String name = qoField.getName();
            if(name.equals("id") ) {
                continue;
            }


            org.springframework.data.mongodb.core.mapping.Field annotation = qoField.getAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
            if (annotation != null) {
                name = StringUtils.isNotEmpty(annotation.value()) ? annotation.value() : qoField.getName();
            }
            Object value = ReflectUtil.getFieldValue(updateObj, qoField);
            if (isSelective) {
                if(name.equals("isDeleted")){
                    if(value==null){
                        update.set(name, BaseIsDeletedEnum.EXISTS);
                        continue;
                    }
                }
                if(name.equals("gmtModified")){
                    if(value==null){
                        update.set(name, new Date());
                        continue;
                    }
                }
                if (ObjectUtil.isNotEmpty(value)) {
                    update.set(name, value);
                    i++;
                }
            }else{
                update.set(name, value);
            }

        }
        if (i==0) {
            throw new IllegalArgumentException("更新字段不能全部为空");
        }
        return update;
    }
    private Query buildWhere(Query query,Q q) {
        if(query==null){
            query = new Query();
        }
        List<Field> qoFields = Arrays.stream(ReflectUtil.getFields(q.getClass())).filter(qoField -> qoField.getAnnotation(Transient.class) == null).collect(Collectors.toList());
        for (Field qoField : qoFields) {
            String name = qoField.getName();
            org.springframework.data.mongodb.core.mapping.Field annotation = qoField.getAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
            if (annotation != null) {
                name = StringUtils.isNotEmpty(annotation.value()) ? annotation.value() : qoField.getName();
            }
            Object value = ReflectUtil.getFieldValue(q, qoField);
            if (ObjectUtil.isNotEmpty(value)) {
                addCriteria(query, name, value);
            }
        }
        return query;
    }

    /**
     * 添加查询条件
     */
    private void addCriteria(Query query, String fieldName, Object value) {
        if (value instanceof String) {
            // 字符串类型：精确匹配
            query.addCriteria(Criteria.where(fieldName).is(value));
        } else if (value instanceof Collection) {
            // 集合类型：IN查询
            query.addCriteria(Criteria.where(fieldName).in((Collection<?>) value));
        } else if (value.getClass().isArray()) {
            // 数组类型：IN查询
            query.addCriteria(Criteria.where(fieldName).in((Object[]) value));
        } else {
            // 其他类型：精确匹配
            query.addCriteria(Criteria.where(fieldName).is(value));
        }
    }
}
